/*-------------------------------------------------------------------------
 *
 * win32_port.h
 *	  特定于 Windows 的兼容性内容。
 *
 * 请注意，在 MinGW 和原生 Windows 构建中会读取此内容，但在 Cygwin 构建中不会。
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 * src/include/port/win32_port.h
 *
 *-------------------------------------------------------------------------
 */
#ifndef PG_WIN32_PORT_H
#define PG_WIN32_PORT_H

/*
 * 始终构建带有 SSPI 支持。保留为 #define，以便
 * 我们将来想要开关来禁用它的时候使用。
 */
#define ENABLE_SSPI 1

/* 在 #include 后取消定义和重新定义 */
#undef mkdir

#undef ERROR

/*
 * VS2013 及更高版本会针对使用旧的 Winsock API 发出警告，
 * 而我们实际上并不想听到这些警告。
 */
#ifdef _MSC_VER
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#endif

/*
 * 如果这个已经被定义，MinGW64 头文件会出错——他们
 * 自己定义了它。
 */
#if !defined(__MINGW64_VERSION_MAJOR) || defined(_MSC_VER)
#define _WINSOCKAPI_
#endif

/*
 * windows.h 包含了许多其他头文件，显著减慢了编译
 * 速度。WIN32_LEAN_AND_MEAN 稍微减少了这一点。如果能
 * 从如此核心的位置删除 windows.h 的包含（以及间接包含），
 * 那会更好，但在那之前...
 */
#define WIN32_LEAN_AND_MEAN

#include <winsock2.h>
#include <ws2tcpip.h>
#include <windows.h>
#undef small
#include <process.h>
#include <signal.h>
#include <direct.h>
#undef near

/* 在下面进行 sys/stat 修改前需要： */
#define fstat microsoft_native_fstat
#define stat microsoft_native_stat
#include <sys/stat.h>
#undef fstat
#undef stat

/* 必须在这里以避免与 windows.h 中的原型冲突 */
#define mkdir(a,b)	mkdir(a)

/* Windows 没有 fsync()，使用 _commit() */
#define fsync(fd) _commit(fd)

/*
 * 出于历史原因，我们允许在 Windows 上将 wal_sync_method 设置为
 * fsync_writethrough，尽管它实际上与 fsync 完全相同
 * （两个代码路径最终都调用 _commit()）。
 */
#define HAVE_FSYNC_WRITETHROUGH
#define FSYNC_WRITETHROUGH_IS_FSYNC

#define USES_WINSOCK

/*
 *	IPC 定义
 */
#undef HAVE_UNION_SEMUN
#define HAVE_UNION_SEMUN 1

#define IPC_RMID 256
#define IPC_CREAT 512
#define IPC_EXCL 1024
#define IPC_PRIVATE 234564
#define IPC_NOWAIT	2048
#define IPC_STAT 4096

#define EACCESS 2048
#ifndef EIDRM
#define EIDRM 4096
#endif

#define SETALL 8192
#define GETNCNT 16384
#define GETVAL 65536
#define SETVAL 131072
#define GETPID 262144



/*
 *	信号相关
 *
 *	对于WIN32，没有wait()调用，所以没有wait()宏
 *	来解释system()的返回值。相反，system()
 *	返回值< 0x100用于exit()终止，而更高的
 *	值用于指示非exit()终止，这与unix风格的信号退出类似
 *	（想想SIGSEGV == STATUS_ACCESS_VIOLATION）。返回值分为 
 *	几组：
 *
 *	https://docs.microsoft.com/en-us/windows-hardware/drivers/kernel/using-ntstatus-values
 *
 *		NT_SUCCESS			0 - 0x3FFFFFFF
 *		NT_INFORMATION		0x40000000 - 0x7FFFFFFF
 *		NT_WARNING			0x80000000 - 0xBFFFFFFF
 *		NT_ERROR			0xC0000000 - 0xFFFFFFFF
 *
 *	实际上，我们不关心system()返回值的严重性，
 *	我们只需要知道这是否是因为exit()或由系统生成的，
 *	似乎值>= 0x100是系统生成的。
 *	请参阅此URL以获取WIN32 STATUS_* 值的列表：
 *
 *		Wine (在我们的错误消息中使用的URL) -
 *			http://source.winehq.org/source/include/ntstatus.h
 *		描述 -
 *			https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-erref/596a1078-e883-4972-9bbc-49e60bebca55
 *
 *	全面的异常列表包含在Windows 驱动程序工具包 (WDK) 的ntstatus.h中。
 *	该列表的一个子集也包含在Windows SDK的winnt.h中。
 *	在包含windows.h之前定义WIN32_NO_STATUS可以帮助避免任何冲突。
 *
 *	总有一天我们可能会想打印最常见的异常的描述，
 *	而不是打印包含文件名。我们可以使用
 *	RtlNtStatusToDosError()并传递给FormatMessage()，
 *	它可以打印错误值的文本，但MinGW不支持
 *	RtlNtStatusToDosError()。
 */
#define WIFEXITED(w)	(((w) & 0XFFFFFF00) == 0)
#define WIFSIGNALED(w)	(!WIFEXITED(w))
#define WEXITSTATUS(w)	(w)
#define WTERMSIG(w)		(w)

#define sigmask(sig) ( 1 << ((sig)-1) )

/* 信号函数返回值 */
#undef SIG_DFL
#undef SIG_ERR
#undef SIG_IGN
#define SIG_DFL ((pqsigfunc)0)
#define SIG_ERR ((pqsigfunc)-1)
#define SIG_IGN ((pqsigfunc)1)

/* 一些额外信号 */
#define SIGHUP				1
#define SIGQUIT				3
#define SIGTRAP				5
#define SIGABRT				22	/* 设置为匹配W32值 -- 而不是UNIX值 */
#define SIGKILL				9
#define SIGPIPE				13
#define SIGALRM				14
#define SIGSTOP				17
#define SIGTSTP				18
#define SIGCONT				19
#define SIGCHLD				20
#define SIGWINCH			28
#define SIGUSR1				30
#define SIGUSR2				31

/*
 * 新版本的MinGW有gettimeofday()并且也声明
 * struct timezone以支持它。
 */
#ifndef HAVE_GETTIMEOFDAY
struct timezone
{
	int			tz_minuteswest; /* 格林威治时间以西的分钟数。 */
	int			tz_dsttime;		/* 如果夏令时曾经生效，则非零。 */
};
#endif

/* for setitimer in backend/port/win32/timer.c */
#define ITIMER_REAL 0
struct itimerval
{
	struct timeval it_interval;
	struct timeval it_value;
};

int			setitimer(int which, const struct itimerval *value, struct itimerval *ovalue);

/* GetFileType() 的便利包装 */
extern DWORD pgwin32_get_file_type(HANDLE hFile);

/*
 * WIN32不提供64位 off_t，但提供操作64位偏移量的函数。
 * 此外，fseek() 对于不可寻址的流可能不会返回错误，因此使用我们的版本来加强该函数。
 */
#define pgoff_t __int64

#ifdef _MSC_VER
extern int	_pgfseeko64(FILE *stream, pgoff_t offset, int origin);
extern pgoff_t _pgftello64(FILE *stream);
#define fseeko(stream, offset, origin) _pgfseeko64(stream, offset, origin)
#define ftello(stream) _pgftello64(stream)
#else
#ifndef fseeko
#define fseeko(stream, offset, origin) fseeko64(stream, offset, origin)
#endif
#ifndef ftello
#define ftello(stream) ftello64(stream)
#endif
#endif

/*
 *	Win32 也没有符号链接，但我们可以在较新的 Win32 版本中
 *	用交叉点模拟它们。
 *
 *	Cygwin 有自己的符号链接，在 Win95/98/ME 上有效，而
 *	交叉点则无效，因此使用这些。我们无法知道
 *	Cygwin 二进制文件将运行在哪种类型的系统上。
 *		注意：某些 CYGWIN 包含可能 #define WIN32。
 */
extern int	pgsymlink(const char *oldpath, const char *newpath);
extern int	pgreadlink(const char *path, char *buf, size_t size);

#define symlink(oldpath, newpath)	pgsymlink(oldpath, newpath)
#define readlink(path, buf, size)	pgreadlink(path, buf, size)

/*
 * <sys/types.h> 的补充。
 *
 * Perl 已经有 uid_t 和 gid_t 的类型定义。
 */
#ifndef PLPERL_HAVE_UID_GID
typedef int uid_t;
typedef int gid_t;
#endif
typedef long key_t;

#ifdef _MSC_VER
typedef int pid_t;
#endif

/*
 * <sys/stat.h> 的补充。
 *
 * 我们必须在这一部分之前引入 sys/stat.h，否则我们的重定义会失效。
 *
 * 在 win32 上，stat() 不保证设置 st_size 字段，因此我们
 * 重新定义为自有实现。请参阅 src/port/win32stat.c。
 *
 * 在 MSVC 中，struct stat 是32位的，因此我们将其重新定义为
 * struct __stat64 的副本。这也修复了 MINGW 编译的结构大小。
 */
struct stat						/* 这应该与 struct __stat64 匹配 */
{
	_dev_t		st_dev;
	_ino_t		st_ino;
	unsigned short st_mode;
	short		st_nlink;
	short		st_uid;
	short		st_gid;
	_dev_t		st_rdev;
	__int64		st_size;
	__time64_t	st_atime;
	__time64_t	st_mtime;
	__time64_t	st_ctime;
};

extern int	_pgfstat64(int fileno, struct stat *buf);
extern int	_pgstat64(const char *name, struct stat *buf);
extern int	_pglstat64(const char *name, struct stat *buf);

#define fstat(fileno, sb)	_pgfstat64(fileno, sb)
#define stat(path, sb)		_pgstat64(path, sb)
#define lstat(path, sb)		_pglstat64(path, sb)

/* 这些宏在旧版 MinGW 和 MSVC 中没有提供 */
#ifndef S_IRUSR
#define S_IRUSR _S_IREAD
#endif
#ifndef S_IWUSR
#define S_IWUSR _S_IWRITE
#endif
#ifndef S_IXUSR
#define S_IXUSR _S_IEXEC
#endif
#ifndef S_IRWXU
#define S_IRWXU (S_IRUSR | S_IWUSR | S_IXUSR)
#endif
#ifndef S_IRGRP
#define S_IRGRP 0
#endif
#ifndef S_IWGRP
#define S_IWGRP 0
#endif
#ifndef S_IXGRP
#define S_IXGRP 0
#endif
#ifndef S_IRWXG
#define S_IRWXG 0
#endif
#ifndef S_IROTH
#define S_IROTH 0
#endif
#ifndef S_IWOTH
#define S_IWOTH 0
#endif
#ifndef S_IXOTH
#define S_IXOTH 0
#endif
#ifndef S_IRWXO
#define S_IRWXO 0
#endif
#ifndef S_ISDIR
#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
#endif
#ifndef S_ISREG
#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
#endif

/*
 * 为了使 lstat() 能够将交叉点报告为符号链接，我们
 * 需要劫持 st_mode 中的一位，因为 MSVC 和 MinGW 都不提供
 * S_ISLNK，并且没有多余的位。我们将偷用用于字符设备的
 * 那个位，因为我们在其他方面不会使用它们。
 */
#ifdef S_ISLNK
#error "S_ISLNK is already defined"
#endif
#ifdef S_IFLNK
#error "S_IFLNK is already defined"
#endif
#define S_IFLNK S_IFCHR
#define S_ISLNK(m) (((m) & S_IFLNK) == S_IFLNK)

/*
 * <fcntl.h> 的补充。
 * 这个值与 MS 头文件中的 _O_NOINHERIT 相同。这是
 * 为了确保我们不会与未来的定义冲突。这意味着
 * 我们自己不能使用 _O_NOINHERIT。
 */
#define O_DSYNC 0x0080

/*
 * <errno.h> 的补充。
 *
 * 我们将与网络相关的 Berkeley 错误符号重新定义为相应的 WSA
 * 常数。这允许 strerror.c 将它们识别为属于 Winsock
 * 错误代码范围，并将它们传递给 win32_socket_strerror()，
 * 因为 Windows 版本的普通 strerror() 无法处理。注意，如果这些名称用于除 Windows Sockets 错误之外的其他任何东西，将会导致问题。
 * 更改此列表时请参阅 TranslateSocketError()。
 */
#undef EAGAIN
#define EAGAIN WSAEWOULDBLOCK
#undef EINTR
#define EINTR WSAEINTR
#undef EMSGSIZE
#define EMSGSIZE WSAEMSGSIZE
#undef EAFNOSUPPORT
#define EAFNOSUPPORT WSAEAFNOSUPPORT
#undef EWOULDBLOCK
#define EWOULDBLOCK WSAEWOULDBLOCK
#undef ECONNABORTED
#define ECONNABORTED WSAECONNABORTED
#undef ECONNRESET
#define ECONNRESET WSAECONNRESET
#undef EINPROGRESS
#define EINPROGRESS WSAEINPROGRESS
#undef EISCONN
#define EISCONN WSAEISCONN
#undef ENOBUFS
#define ENOBUFS WSAENOBUFS
#undef EPROTONOSUPPORT
#define EPROTONOSUPPORT WSAEPROTONOSUPPORT
#undef ECONNREFUSED
#define ECONNREFUSED WSAECONNREFUSED
#undef ENOTSOCK
#define ENOTSOCK WSAENOTSOCK
#undef EOPNOTSUPP
#define EOPNOTSUPP WSAEOPNOTSUPP
#undef EADDRINUSE
#define EADDRINUSE WSAEADDRINUSE
#undef EADDRNOTAVAIL
#define EADDRNOTAVAIL WSAEADDRNOTAVAIL
#undef EHOSTDOWN
#define EHOSTDOWN WSAEHOSTDOWN
#undef EHOSTUNREACH
#define EHOSTUNREACH WSAEHOSTUNREACH
#undef ENETDOWN
#define ENETDOWN WSAENETDOWN
#undef ENETRESET
#define ENETRESET WSAENETRESET
#undef ENETUNREACH
#define ENETUNREACH WSAENETUNREACH
#undef ENOTCONN
#define ENOTCONN WSAENOTCONN
#undef ETIMEDOUT
#define ETIMEDOUT WSAETIMEDOUT

/*
 * 区域设置相关内容。
 *
 * 扩展的区域设置函数，带有多余的下划线前缀。
 * (这些 API 仍然由 Microsoft 完整文档化。)
 */
#define locale_t _locale_t
#define tolower_l _tolower_l
#define toupper_l _toupper_l
#define towlower_l _towlower_l
#define towupper_l _towupper_l
#define isdigit_l _isdigit_l
#define iswdigit_l _iswdigit_l
#define isalpha_l _isalpha_l
#define iswalpha_l _iswalpha_l
#define isalnum_l _isalnum_l
#define iswalnum_l _iswalnum_l
#define isupper_l _isupper_l
#define iswupper_l _iswupper_l
#define islower_l _islower_l
#define iswlower_l _iswlower_l
#define isgraph_l _isgraph_l
#define iswgraph_l _iswgraph_l
#define isprint_l _isprint_l
#define iswprint_l _iswprint_l
#define ispunct_l _ispunct_l
#define iswpunct_l _iswpunct_l
#define isspace_l _isspace_l
#define iswspace_l _iswspace_l
#define strcoll_l _strcoll_l
#define strxfrm_l _strxfrm_l
#define wcscoll_l _wcscoll_l
#define wcstombs_l _wcstombs_l
#define mbstowcs_l _mbstowcs_l

/*
 * libintl 版本 >= 0.18？试图用一个宏替换 setlocale() 
 * 为它们自己的版本。如果该宏存在，请删除它，因为
 * 当后端和 libintl 使用不同版本的 msvcrt 时，
 * 它最终会调用错误的版本。
 */
#if defined(setlocale)
#undef setlocale
#endif

/*
 * 定义我们自己的 setlocale() 包装宏，以解决 Windows 本地 setlocale()
 * 函数中的错误。
 */
extern char *pgwin32_setlocale(int category, const char *locale);

#define setlocale(a,b) pgwin32_setlocale(a,b)


/* 在 backend/port/win32/signal.c */
extern PGDLLIMPORT volatile int pg_signal_queue;
extern PGDLLIMPORT int pg_signal_mask;
extern PGDLLIMPORT HANDLE pgwin32_signal_event;
extern PGDLLIMPORT HANDLE pgwin32_initial_signal_pipe;

#define UNBLOCKED_SIGNAL_QUEUE()	(pg_signal_queue & ~pg_signal_mask)
#define PG_SIGNAL_COUNT 32

extern void pgwin32_signal_initialize(void);
extern HANDLE pgwin32_create_signal_listener(pid_t pid);
extern void pgwin32_dispatch_queued_signals(void);
extern void pg_queue_signal(int signum);

/* 在 src/port/kill.c */
#define kill(pid,sig)	pgkill(pid,sig)
extern int	pgkill(int pid, int sig);

/* 在 backend/port/win32/socket.c */
#ifndef FRONTEND
#define socket(af, type, protocol) pgwin32_socket(af, type, protocol)
#define bind(s, addr, addrlen) pgwin32_bind(s, addr, addrlen)
#define listen(s, backlog) pgwin32_listen(s, backlog)
#define accept(s, addr, addrlen) pgwin32_accept(s, addr, addrlen)
#define connect(s, name, namelen) pgwin32_connect(s, name, namelen)
#define select(n, r, w, e, timeout) pgwin32_select(n, r, w, e, timeout)
#define recv(s, buf, len, flags) pgwin32_recv(s, buf, len, flags)
#define send(s, buf, len, flags) pgwin32_send(s, buf, len, flags)

extern SOCKET pgwin32_socket(int af, int type, int protocol);
extern int	pgwin32_bind(SOCKET s, struct sockaddr *addr, int addrlen);
extern int	pgwin32_listen(SOCKET s, int backlog);
extern SOCKET pgwin32_accept(SOCKET s, struct sockaddr *addr, int *addrlen);
extern int	pgwin32_connect(SOCKET s, const struct sockaddr *name, int namelen);
extern int	pgwin32_select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, const struct timeval *timeout);
extern int	pgwin32_recv(SOCKET s, char *buf, int len, int flags);
extern int	pgwin32_send(SOCKET s, const void *buf, int len, int flags);
extern int	pgwin32_waitforsinglesocket(SOCKET s, int what, int timeout);

extern PGDLLIMPORT int pgwin32_noblock;

#endif							/* FRONTEND */

/* 在 backend/port/win32_shmem.c */
extern int	pgwin32_ReserveSharedMemoryRegion(HANDLE);

/* 在 backend/port/win32/crashdump.c */
extern void pgwin32_install_crashdump_handler(void);

/* 在 port/win32error.c */
extern void _dosmaperr(unsigned long);

/* 在 port/win32env.c */
extern int	pgwin32_putenv(const char *);
extern int	pgwin32_setenv(const char *name, const char *value, int overwrite);
extern int	pgwin32_unsetenv(const char *name);

#define putenv(x) pgwin32_putenv(x)
#define setenv(x,y,z) pgwin32_setenv(x,y,z)
#define unsetenv(x) pgwin32_unsetenv(x)

/* 在 port/win32security.c */
extern int	pgwin32_is_service(void);
extern int	pgwin32_is_admin(void);

/* Windows 安全令牌操作（在 src/common/exec.c 中） */
extern BOOL AddUserToTokenDacl(HANDLE hToken);

/* 在 MinGW 头文件中存在的内容，但需要添加到 MSVC 中 */
#ifdef _MSC_VER

#ifndef _WIN64
typedef long ssize_t;
#else
typedef __int64 ssize_t;
#endif

typedef unsigned short mode_t;

#define F_OK 0
#define W_OK 2
#define R_OK 4

#endif							/* _MSC_VER */

#if (defined(_MSC_VER) && (_MSC_VER < 1900)) || \
	defined(__MINGW32__) || defined(__MINGW64__)
/*
 * VS2013 有一个 strtof()，似乎对于有效输入给出正确答案，
 * 即使在舍入边缘情况下，但对超出范围的输入处理不正确。解决此问题。
 *
 * Mingw 声称有一个 strtof，且我对其源代码的阅读表明
 * 它应该可以正常工作（并且不需要这个黑客），但回归测试
 * 结果与我不符；这是否是版本问题尚不明了。不过，使用我们的包装（和
 * 错误舍入输入变体文件，已经要求支持古老系统）不会使事情变得更糟，除了在读取零时的轻微性能损失。
 *
 * 另请参阅 cygwin.h 以获取另一个实例。
 */
#define HAVE_BUGGY_STRTOF 1
#endif

#endif							/* PG_WIN32_PORT_H */
